using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Comteck.Winforms.Extensions {
  /// <summary>
  /// https://github.com/houseofcat/Library/blob/master/HouseofCat.Library/Extensions/IEnumerableExtensions.cs
  /// </summary>
  public static class IEnumerableExtensions {
    public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> funcBody, int maxDoP = 4) {
      async Task AwaitPartition(IEnumerator<T> partition) {
        using (partition) {
          while (partition.MoveNext()) { await funcBody(partition.Current); }
        }
      }

      return Task.WhenAll(
          Partitioner
              .Create(source)
              .GetPartitions(maxDoP)
              .AsParallel()
              .Select(p => AwaitPartition(p)));
    }

    public static Task ParallelForEachAsync<T1, T2>(this IEnumerable<T1> source, Func<T1, T2, Task> funcBody, T2 inputClass, int maxDoP = 4) {
      async Task AwaitPartition(IEnumerator<T1> partition) {
        using (partition) {
          while (partition.MoveNext()) { await funcBody(partition.Current, inputClass); }
        }
      }

      return Task.WhenAll(
          Partitioner
              .Create(source)
              .GetPartitions(maxDoP)
              .AsParallel()
              .Select(p => AwaitPartition(p)));
    }

    public static Task ParallelForEachAsync<T1, T2, T3>(this IEnumerable<T1> source, Func<T1, T2, T3, Task> funcBody, T2 inputClass, T3 secondInputClass, int maxDoP = 4) {
      async Task AwaitPartition(IEnumerator<T1> partition) {
        using (partition) {
          while (partition.MoveNext()) { await funcBody(partition.Current, inputClass, secondInputClass); }
        }
      }

      return Task.WhenAll(
          Partitioner
              .Create(source)
              .GetPartitions(maxDoP)
              .AsParallel()
              .Select(p => AwaitPartition(p)));
    }

    public static Task ParallelForEachAsync<T1, T2, T3, T4>(this IEnumerable<T1> source, Func<T1, T2, T3, T4, Task> funcBody, T2 inputClass, T3 secondInputClass, T4 thirdInputClass, int maxDoP = 4) {
      async Task AwaitPartition(IEnumerator<T1> partition) {
        using (partition) {
          while (partition.MoveNext()) { await funcBody(partition.Current, inputClass, secondInputClass, thirdInputClass); }
        }
      }

      return Task.WhenAll(
          Partitioner
              .Create(source)
              .GetPartitions(maxDoP)
              .AsParallel()
              .Select(p => AwaitPartition(p)));
    }

    public static int CountDistinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
      => source.Select(keySelector).Distinct().Count();

    public static List<T> AsList<T>(this T model) => new List<T> { model };


    /// <summary>
    /// 对一个集合按照分页大小分割
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <param name="size"></param>
    /// <returns></returns>
    public static List<List<T>> SplitPageList<T>(this List<T> list, int size) {
      // 总记录数
      var total = list.Count;

      // 总页数
      var pageCount = (total + size - 1) / Math.Max(size, 1);

      var result = new List<List<T>>();

      for (var page = 1; page <= pageCount; page++) {
        var subList = list.Skip((Math.Max(page, 1) - 1) * size).Take(size).ToList();
        result.Add(subList);
      }

      return result;
    }

    /// <summary>
    /// 当数组为空时转换成null
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list"></param>
    /// <returns></returns>
    public static List<T> EmptyToNull<T>(this List<T> list) {
      if (list == null || list.Count > 0) {
        return list;
      }

      return null;
    }

  }

}
